home *** CD-ROM | disk | FTP | other *** search
/ Aminet 48 / Aminet 48 (2002)(GTI - Schatztruhe)[!][Apr 2002].iso / Aminet / text / edit / vim60src.lha / Vim / vim60 / src / undo.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-08-24  |  32.0 KB  |  1,287 lines

  1. /* vi:set ts=8 sts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved    by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  * See README.txt for an overview of the Vim source code.
  8.  */
  9.  
  10. /*
  11.  * undo.c: multi level undo facility
  12.  *
  13.  * The saved lines are stored in a list of lists (one for each buffer):
  14.  *
  15.  * b_u_oldhead------------------------------------------------+
  16.  *                                  |
  17.  *                                  V
  18.  *          +--------------+    +--------------+      +--------------+
  19.  * b_u_newhead--->| u_header     |    | u_header     |      | u_header     |
  20.  *          |    uh_next------>|     uh_next------>|    uh_next---->NULL
  21.  *       NULL<--------uh_prev  |<---------uh_prev  |<---------uh_prev  |
  22.  *          |    uh_entry |    |     uh_entry |      |    uh_entry |
  23.  *          +--------|-----+    +--------|-----+      +--------|-----+
  24.  *               |               |           |
  25.  *               V               V           V
  26.  *          +--------------+    +--------------+      +--------------+
  27.  *          | u_entry     |    | u_entry      |      | u_entry     |
  28.  *          |    ue_next  |    |     ue_next  |      |    ue_next  |
  29.  *          +--------|-----+    +--------|-----+      +--------|-----+
  30.  *               |               |           |
  31.  *               V               V           V
  32.  *          +--------------+          NULL          NULL
  33.  *          | u_entry     |
  34.  *          |    ue_next  |
  35.  *          +--------|-----+
  36.  *               |
  37.  *               V
  38.  *              etc.
  39.  *
  40.  * Each u_entry list contains the information for one undo or redo.
  41.  * curbuf->b_u_curhead points to the header of the last undo (the next redo),
  42.  * or is NULL if nothing has been undone.
  43.  *
  44.  * All data is allocated with u_alloc_line(), thus it will be freed as soon as
  45.  * we switch files!
  46.  */
  47.  
  48. #include "vim.h"
  49.  
  50. static u_entry_T *u_get_headentry __ARGS((void));
  51. static void u_getbot __ARGS((void));
  52. static int u_savecommon __ARGS((linenr_T, linenr_T, linenr_T));
  53. static void u_doit __ARGS((int count));
  54. static void u_undoredo __ARGS((void));
  55. static void u_undo_end __ARGS((void));
  56. static void u_freelist __ARGS((struct u_header *));
  57. static void u_freeentry __ARGS((u_entry_T *, long));
  58.  
  59. static char_u *u_blockalloc __ARGS((long_u));
  60. static void u_free_line __ARGS((char_u *, int keep));
  61. static char_u *u_alloc_line __ARGS((unsigned));
  62. static char_u *u_save_line __ARGS((linenr_T));
  63.  
  64. static long    u_newcount, u_oldcount;
  65.  
  66. /*
  67.  * When 'u' flag included in 'cpoptions', we behave like vi.  Need to remember
  68.  * the action that "u" should do.
  69.  */
  70. static int    undo_undoes = FALSE;
  71.  
  72. /*
  73.  * save the current line for both the "u" and "U" command
  74.  */
  75.     int
  76. u_save_cursor()
  77. {
  78.     return (u_save((linenr_T)(curwin->w_cursor.lnum - 1),
  79.                       (linenr_T)(curwin->w_cursor.lnum + 1)));
  80. }
  81.  
  82. /*
  83.  * Save the lines between "top" and "bot" for both the "u" and "U" command.
  84.  * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
  85.  * Returns FAIL when lines could not be saved, OK otherwise.
  86.  */
  87.     int
  88. u_save(top, bot)
  89.     linenr_T top, bot;
  90. {
  91.     if (undo_off)
  92.     return OK;
  93.  
  94.     if (top > curbuf->b_ml.ml_line_count ||
  95.                 top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
  96.     return FALSE;    /* rely on caller to do error messages */
  97.  
  98.     if (top + 2 == bot)
  99.     u_saveline((linenr_T)(top + 1));
  100.  
  101.     return (u_savecommon(top, bot, (linenr_T)0));
  102. }
  103.  
  104. /*
  105.  * save the line "lnum" (used by ":s" and "~" command)
  106.  * The line is replaced, so the new bottom line is lnum + 1.
  107.  */
  108.     int
  109. u_savesub(lnum)
  110.     linenr_T    lnum;
  111. {
  112.     if (undo_off)
  113.     return OK;
  114.  
  115.     return (u_savecommon(lnum - 1, lnum + 1, lnum + 1));
  116. }
  117.  
  118. /*
  119.  * a new line is inserted before line "lnum" (used by :s command)
  120.  * The line is inserted, so the new bottom line is lnum + 1.
  121.  */
  122.     int
  123. u_inssub(lnum)
  124.     linenr_T    lnum;
  125. {
  126.     if (undo_off)
  127.     return OK;
  128.  
  129.     return (u_savecommon(lnum - 1, lnum, lnum + 1));
  130. }
  131.  
  132. /*
  133.  * save the lines "lnum" - "lnum" + nlines (used by delete command)
  134.  * The lines are deleted, so the new bottom line is lnum, unless the buffer
  135.  * becomes empty.
  136.  */
  137.     int
  138. u_savedel(lnum, nlines)
  139.     linenr_T    lnum;
  140.     long    nlines;
  141. {
  142.     if (undo_off)
  143.     return OK;
  144.  
  145.     return (u_savecommon(lnum - 1, lnum + nlines,
  146.             nlines == curbuf->b_ml.ml_line_count ? 2 : lnum));
  147. }
  148.  
  149.     static int
  150. u_savecommon(top, bot, newbot)
  151.     linenr_T    top, bot;
  152.     linenr_T    newbot;
  153. {
  154.     linenr_T        lnum;
  155.     long        i;
  156.     struct u_header    *uhp;
  157.     u_entry_T        *uep;
  158.     long        size;
  159.  
  160.     /*
  161.      * Don't allow changes when 'modifiable' is off.  Letting the
  162.      * undo fail is a crude way to make all change commands fail.
  163.      */
  164.     if (!curbuf->b_p_ma)
  165.     {
  166.     EMSG(_(e_modifiable));
  167.     return FAIL;
  168.     }
  169.  
  170. #ifdef HAVE_SANDBOX
  171.     /*
  172.      * In the sandbox it's not allowed to change the text.  Letting the
  173.      * undo fail is a crude way to make all change commands fail.
  174.      */
  175.     if (sandbox != 0)
  176.     {
  177.     EMSG(_(e_sandbox));
  178.     return FAIL;
  179.     }
  180. #endif
  181.  
  182. #ifdef FEAT_AUTOCMD
  183.     /*
  184.      * Saving text for undo means we are going to make a change.  Give a
  185.      * warning for a read-only file before making the change, so that the
  186.      * FileChangedRO event can replace the buffer with a read-write version
  187.      * (e.g., obtained from a source control system).
  188.      */
  189.     change_warning(0);
  190. #endif
  191.  
  192.     size = bot - top - 1;
  193.  
  194.     /*
  195.      * if curbuf->b_u_synced == TRUE make a new header
  196.      */
  197.     if (curbuf->b_u_synced)
  198.     {
  199.     /*
  200.      * if we undid more than we redid, free the entry lists before and
  201.      * including curbuf->b_u_curhead
  202.      */
  203.     while (curbuf->b_u_curhead != NULL)
  204.         u_freelist(curbuf->b_u_newhead);
  205.  
  206.     /*
  207.      * free headers to keep the size right
  208.      */
  209.     while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL)
  210.         u_freelist(curbuf->b_u_oldhead);
  211.  
  212.     if (p_ul < 0)        /* no undo at all */
  213.         return OK;
  214.  
  215.     /*
  216.      * make a new header entry
  217.      */
  218.     uhp = (struct u_header *)u_alloc_line((unsigned)
  219.                              sizeof(struct u_header));
  220.     if (uhp == NULL)
  221.         goto nomem;
  222.     uhp->uh_prev = NULL;
  223.     uhp->uh_next = curbuf->b_u_newhead;
  224.     if (curbuf->b_u_newhead != NULL)
  225.         curbuf->b_u_newhead->uh_prev = uhp;
  226.     uhp->uh_entry = NULL;
  227.     uhp->uh_cursor = curwin->w_cursor;    /* save cursor pos. for undo */
  228. #ifdef FEAT_VIRTUALEDIT
  229.     if (virtual_active() && curwin->w_cursor.coladd > 0)
  230.         uhp->uh_cursor_vcol = getviscol();
  231.     else
  232.         uhp->uh_cursor_vcol = -1;
  233. #endif
  234.  
  235.     /* save changed and buffer empty flag for undo */
  236.     uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
  237.                ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
  238.  
  239.     /* save named marks for undo */
  240.     mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
  241.     curbuf->b_u_newhead = uhp;
  242.     if (curbuf->b_u_oldhead == NULL)
  243.         curbuf->b_u_oldhead = uhp;
  244.     ++curbuf->b_u_numhead;
  245.     }
  246.     else
  247.     {
  248.     /*
  249.      * When saving a single line, and it has been saved just before, it
  250.      * doesn't make sense saving it again.  Saves a lot of memory when
  251.      * making lots of changes inside the same line.
  252.      * This is only possible if the previous change didn't increase or
  253.      * decrease the number of lines.
  254.      * Check the ten last changes.  More doesn't make sense and takes too
  255.      * long.
  256.      */
  257.     if (size == 1)
  258.     {
  259.         uep = u_get_headentry();
  260.         for (i = 0; i < 10; ++i)
  261.         {
  262.         if (uep == NULL)
  263.             break;
  264.  
  265.         /* If lines have been inserted/deleted we give up. */
  266.         if (uep->ue_lcount == 0
  267.             ? (uep->ue_top + uep->ue_size + 1
  268.                 != (uep->ue_bot == 0
  269.                 ? curbuf->b_ml.ml_line_count + 1
  270.                 : uep->ue_bot))
  271.             : uep->ue_lcount != curbuf->b_ml.ml_line_count)
  272.             break;
  273.  
  274.         /* If it's the same line we can skip saving it again. */
  275.         if (uep->ue_size == 1 && uep->ue_top == top)
  276.         {
  277.             /* The line count might change. */
  278.             uep->ue_lcount = 0;
  279.             if (newbot != 0)
  280.             uep->ue_bot = newbot;
  281.             else if (bot > curbuf->b_ml.ml_line_count)
  282.             uep->ue_bot = 0;
  283.             else
  284.             uep->ue_lcount = curbuf->b_ml.ml_line_count;
  285.             return OK;
  286.         }
  287.         uep = uep->ue_next;
  288.         }
  289.     }
  290.  
  291.     /* find line number for ue_bot for previous u_save() */
  292.     u_getbot();
  293.     }
  294.  
  295. #if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__)
  296.     /*
  297.      * With Amiga and MSDOS 16 bit we can't handle big undo's, because
  298.      * then u_alloc_line would have to allocate a block larger than 32K
  299.      */
  300.     if (size >= 8000)
  301.     goto nomem;
  302. #endif
  303.  
  304.     /*
  305.      * add lines in front of entry list
  306.      */
  307.     uep = (u_entry_T *)u_alloc_line((unsigned)sizeof(u_entry_T));
  308.     if (uep == NULL)
  309.     goto nomem;
  310.  
  311.     uep->ue_size = size;
  312.     uep->ue_top = top;
  313.     uep->ue_lcount = 0;
  314.     if (newbot)
  315.     uep->ue_bot = newbot;
  316.     /*
  317.      * Use 0 for ue_bot if bot is below last line.
  318.      * Otherwise we have to compute ue_bot later.
  319.      */
  320.     else if (bot > curbuf->b_ml.ml_line_count)
  321.     uep->ue_bot = 0;
  322.     else
  323.     uep->ue_lcount = curbuf->b_ml.ml_line_count;
  324.  
  325.     if (size)
  326.     {
  327.     if ((uep->ue_array = (char_u **)u_alloc_line(
  328.                 (unsigned)(sizeof(char_u *) * size))) == NULL)
  329.     {
  330.         u_freeentry(uep, 0L);
  331.         goto nomem;
  332.     }
  333.     for (i = 0, lnum = top + 1; i < size; ++i)
  334.     {
  335.         if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL)
  336.         {
  337.         u_freeentry(uep, i);
  338.         goto nomem;
  339.         }
  340.     }
  341.     }
  342.     uep->ue_next = curbuf->b_u_newhead->uh_entry;
  343.     curbuf->b_u_newhead->uh_entry = uep;
  344.     curbuf->b_u_synced = FALSE;
  345.     undo_undoes = FALSE;
  346.  
  347.     return OK;
  348.  
  349. nomem:
  350.     msg_silent = 0;    /* must display the prompt */
  351.     if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
  352.                                        == 'y')
  353.     {
  354.     undo_off = TRUE;        /* will be reset when character typed */
  355.     return OK;
  356.     }
  357.     do_outofmem_msg((long_u)0);
  358.     return FAIL;
  359. }
  360.  
  361. /*
  362.  * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
  363.  * If 'cpoptions' does not contain 'u': Always undo.
  364.  */
  365.     void
  366. u_undo(count)
  367.     int count;
  368. {
  369.     /*
  370.      * If we get an undo command while executing a macro, we behave like the
  371.      * original vi. If this happens twice in one macro the result will not
  372.      * be compatible.
  373.      */
  374.     if (curbuf->b_u_synced == FALSE)
  375.     {
  376.     u_sync();
  377.     count = 1;
  378.     }
  379.  
  380.     if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
  381.     undo_undoes = TRUE;
  382.     else
  383.     undo_undoes = !undo_undoes;
  384.     u_doit(count);
  385. }
  386.  
  387. /*
  388.  * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
  389.  * If 'cpoptions' does not contain 'u': Always redo.
  390.  */
  391.     void
  392. u_redo(count)
  393.     int count;
  394. {
  395.     if (vim_strchr(p_cpo, CPO_UNDO) == NULL)
  396.     undo_undoes = FALSE;
  397.     u_doit(count);
  398. }
  399.  
  400. /*
  401.  * Undo or redo, depending on 'undo_undoes', 'count' times.
  402.  */
  403.     static void
  404. u_doit(count)
  405.     int count;
  406. {
  407.     /* Don't allow changes when 'modifiable' is off. */
  408.     if (!curbuf->b_p_ma)
  409.     {
  410.     EMSG(_(e_modifiable));
  411.     return;
  412.     }
  413. #ifdef HAVE_SANDBOX
  414.     /* In the sandbox it's not allowed to change the text. */
  415.     if (sandbox != 0)
  416.     {
  417.     EMSG(_(e_sandbox));
  418.     return;
  419.     }
  420. #endif
  421.  
  422.     u_newcount = 0;
  423.     u_oldcount = 0;
  424.     while (count--)
  425.     {
  426.     if (undo_undoes)
  427.     {
  428.         if (curbuf->b_u_curhead == NULL)        /* first undo */
  429.         curbuf->b_u_curhead = curbuf->b_u_newhead;
  430.         else if (p_ul > 0)                /* multi level undo */
  431.         /* get next undo */
  432.         curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next;
  433.         /* nothing to undo */
  434.         if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL)
  435.         {
  436.         /* stick curbuf->b_u_curhead at end */
  437.         curbuf->b_u_curhead = curbuf->b_u_oldhead;
  438.         beep_flush();
  439.         break;
  440.         }
  441.  
  442.         u_undoredo();
  443.     }
  444.     else
  445.     {
  446.         if (curbuf->b_u_curhead == NULL || p_ul <= 0)
  447.         {
  448.         beep_flush();    /* nothing to redo */
  449.         break;
  450.         }
  451.  
  452.         u_undoredo();
  453.         /* advance for next redo */
  454.         curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev;
  455.     }
  456.     }
  457.     u_undo_end();
  458. }
  459.  
  460. /*
  461.  * u_undoredo: common code for undo and redo
  462.  *
  463.  * The lines in the file are replaced by the lines in the entry list at
  464.  * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry
  465.  * list for the next undo/redo.
  466.  */
  467.     static void
  468. u_undoredo()
  469. {
  470.     char_u    **newarray = NULL;
  471.     linenr_T    oldsize;
  472.     linenr_T    newsize;
  473.     linenr_T    top, bot;
  474.     linenr_T    lnum;
  475.     linenr_T    newlnum = MAXLNUM;
  476.     long    i;
  477.     u_entry_T    *uep, *nuep;
  478.     u_entry_T    *newlist = NULL;
  479.     int        old_flags;
  480.     int        new_flags;
  481.     pos_T    namedm[NMARKS];
  482.     int        empty_buffer;            /* buffer became empty */
  483.  
  484.     old_flags = curbuf->b_u_curhead->uh_flags;
  485.     new_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
  486.            ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
  487.     setpcmark();
  488.  
  489.     /*
  490.      * save marks before undo/redo
  491.      */
  492.     mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
  493.     curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
  494.     curbuf->b_op_start.col = 0;
  495.     curbuf->b_op_end.lnum = 0;
  496.     curbuf->b_op_end.col = 0;
  497.  
  498.     for (uep = curbuf->b_u_curhead->uh_entry; uep != NULL; uep = nuep)
  499.     {
  500.     top = uep->ue_top;
  501.     bot = uep->ue_bot;
  502.     if (bot == 0)
  503.         bot = curbuf->b_ml.ml_line_count + 1;
  504.     if (top > curbuf->b_ml.ml_line_count || top >= bot || bot > curbuf->b_ml.ml_line_count + 1)
  505.     {
  506.         EMSG(_("E438: u_undo: line numbers wrong"));
  507.         changed();        /* don't want UNCHANGED now */
  508.         return;
  509.     }
  510.  
  511.     if (top < newlnum)
  512.     {
  513.         newlnum = top;
  514.         curwin->w_cursor.lnum = top + 1;
  515.     }
  516.     oldsize = bot - top - 1;    /* number of lines before undo */
  517.     newsize = uep->ue_size;        /* number of lines after undo */
  518.  
  519.     empty_buffer = FALSE;
  520.  
  521.     /* delete the lines between top and bot and save them in newarray */
  522.     if (oldsize)
  523.     {
  524.         if ((newarray = (char_u **)u_alloc_line(
  525.                 (unsigned)(sizeof(char_u *) * oldsize))) == NULL)
  526.         {
  527.         do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize));
  528.         /*
  529.          * We have messed up the entry list, repair is impossible.
  530.          * we have to free the rest of the list.
  531.          */
  532.         while (uep != NULL)
  533.         {
  534.             nuep = uep->ue_next;
  535.             u_freeentry(uep, uep->ue_size);
  536.             uep = nuep;
  537.         }
  538.         break;
  539.         }
  540.         /* delete backwards, it goes faster in most cases */
  541.         for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum)
  542.         {
  543.         /* what can we do when we run out of memory? */
  544.         if ((newarray[i] = u_save_line(lnum)) == NULL)
  545.             do_outofmem_msg((long_u)0);
  546.         /* remember we deleted the last line in the buffer, and a
  547.          * dummy empty line will be inserted */
  548.         if (curbuf->b_ml.ml_line_count == 1)
  549.             empty_buffer = TRUE;
  550.         ml_delete(lnum, FALSE);
  551.         }
  552.     }
  553.  
  554.     /* insert the lines in u_array between top and bot */
  555.     if (newsize)
  556.     {
  557.         for (lnum = top, i = 0; i < newsize; ++i, ++lnum)
  558.         {
  559.         /*
  560.          * If the file is empty, there is an empty line 1 that we
  561.          * should get rid of, by replacing it with the new line
  562.          */
  563.         if (empty_buffer && lnum == 0)
  564.             ml_replace((linenr_T)1, uep->ue_array[i], TRUE);
  565.         else
  566.             ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE);
  567.         u_free_line(uep->ue_array[i], FALSE);
  568.         }
  569.         u_free_line((char_u *)uep->ue_array, FALSE);
  570.     }
  571.  
  572.     /* adjust marks */
  573.     if (oldsize != newsize)
  574.     {
  575.         mark_adjust(top + 1, top + oldsize, (long)MAXLNUM,
  576.                            (long)newsize - (long)oldsize);
  577.         if (curbuf->b_op_start.lnum > top + oldsize)
  578.         curbuf->b_op_start.lnum += newsize - oldsize;
  579.         if (curbuf->b_op_end.lnum > top + oldsize)
  580.         curbuf->b_op_end.lnum += newsize - oldsize;
  581.     }
  582.  
  583.     changed_lines(top + 1, 0, bot, newsize - oldsize);
  584.  
  585.     /* set '[ and '] mark */
  586.     if (top + 1 < curbuf->b_op_start.lnum)
  587.         curbuf->b_op_start.lnum = top + 1;
  588.     if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum)
  589.         curbuf->b_op_end.lnum = top + 1;
  590.     else if (top + newsize > curbuf->b_op_end.lnum)
  591.         curbuf->b_op_end.lnum = top + newsize;
  592.  
  593.     u_newcount += newsize;
  594.     u_oldcount += oldsize;
  595.     uep->ue_size = oldsize;
  596.     uep->ue_array = newarray;
  597.     uep->ue_bot = top + newsize + 1;
  598.  
  599.     /*
  600.      * insert this entry in front of the new entry list
  601.      */
  602.     nuep = uep->ue_next;
  603.     uep->ue_next = newlist;
  604.     newlist = uep;
  605.     }
  606.  
  607.     curbuf->b_u_curhead->uh_entry = newlist;
  608.     curbuf->b_u_curhead->uh_flags = new_flags;
  609.     if ((old_flags & UH_EMPTYBUF) && bufempty())
  610.     curbuf->b_ml.ml_flags |= ML_EMPTY;
  611.     if (old_flags & UH_CHANGED)
  612.     changed();
  613.     else
  614.     unchanged(curbuf, FALSE);
  615.  
  616.     /*
  617.      * restore marks from before undo/redo
  618.      */
  619.     for (i = 0; i < NMARKS; ++i)
  620.     if (curbuf->b_u_curhead->uh_namedm[i].lnum)
  621.     {
  622.         curbuf->b_namedm[i] = curbuf->b_u_curhead->uh_namedm[i];
  623.         curbuf->b_u_curhead->uh_namedm[i] = namedm[i];
  624.     }
  625.  
  626.     /*
  627.      * If the cursor is only off by one line, put it at the same position as
  628.      * before starting the change (for the "o" command).
  629.      * Otherwise the cursor should go to the first undone line.
  630.      */
  631.     if (curbuf->b_u_curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
  632.                          && curwin->w_cursor.lnum > 1)
  633.     --curwin->w_cursor.lnum;
  634.     if (curbuf->b_u_curhead->uh_cursor.lnum == curwin->w_cursor.lnum)
  635.     {
  636.     curwin->w_cursor.col = curbuf->b_u_curhead->uh_cursor.col;
  637. #ifdef FEAT_VIRTUALEDIT
  638.     if (curbuf->b_u_curhead->uh_cursor_vcol >= 0)
  639.         coladvance((colnr_T)curbuf->b_u_curhead->uh_cursor_vcol);
  640.     else
  641.         curwin->w_cursor.coladd = 0;
  642. #endif
  643.     }
  644.     else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count)
  645.     beginline(BL_SOL | BL_FIX);
  646.     else
  647.     {
  648.     /* We get here with the current cursor line being past the end (eg
  649.      * after adding lines at the end of the file, and then undoing it).
  650.      * check_cursor() will move the cursor to the last line.  Move it to
  651.      * the first column here. */
  652.     curwin->w_cursor.col = 0;
  653. #ifdef FEAT_VIRTUALEDIT
  654.     curwin->w_cursor.coladd = 0;
  655. #endif
  656.     }
  657.  
  658.     /* Make sure the cursor is on an existing line and column. */
  659.     check_cursor();
  660. }
  661.  
  662. /*
  663.  * If we deleted or added lines, report the number of less/more lines.
  664.  * Otherwise, report the number of changes (this may be incorrect
  665.  * in some cases, but it's better than nothing).
  666.  */
  667.     static void
  668. u_undo_end()
  669. {
  670.     if ((u_oldcount -= u_newcount) != 0)
  671.     msgmore(-u_oldcount);
  672.     else if (u_newcount > p_report)
  673.     {
  674.     if (u_newcount == 1)
  675.         MSG(_("1 change"));
  676.     else
  677.         smsg((char_u *)_("%ld changes"), u_newcount);
  678.     }
  679. #ifdef FEAT_FOLDING
  680.     if ((fdo_flags & FDO_UNDO) && KeyTyped)
  681.     foldOpenCursor();
  682. #endif
  683. }
  684.  
  685. /*
  686.  * u_sync: stop adding to the current entry list
  687.  */
  688.     void
  689. u_sync()
  690. {
  691.     if (curbuf->b_u_synced)
  692.     return;            /* already synced */
  693.     u_getbot();            /* compute ue_bot of previous u_save */
  694.     curbuf->b_u_curhead = NULL;
  695. }
  696.  
  697. /*
  698.  * Called after writing the file and setting b_changed to FALSE.
  699.  * Now an undo means that the buffer is modified.
  700.  */
  701.     void
  702. u_unchanged(buf)
  703.     buf_T    *buf;
  704. {
  705.     struct u_header    *uh;
  706.  
  707.     for (uh = buf->b_u_newhead; uh; uh = uh->uh_next)
  708.     uh->uh_flags |= UH_CHANGED;
  709.     buf->b_did_warn = FALSE;
  710. }
  711.  
  712. /*
  713.  * Get pointer to last added entry.
  714.  * If it's not valid, give an error message and return NULL.
  715.  */
  716.     static u_entry_T *
  717. u_get_headentry()
  718. {
  719.     if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL)
  720.     {
  721.     EMSG(_("E439: undo list corrupt"));
  722.     return NULL;
  723.     }
  724.     return curbuf->b_u_newhead->uh_entry;
  725. }
  726.  
  727. /*
  728.  * u_getbot(): compute the line number of the previous u_save
  729.  *        It is called only when b_u_synced is FALSE.
  730.  */
  731.     static void
  732. u_getbot()
  733. {
  734.     u_entry_T    *uep;
  735.  
  736.     uep = u_get_headentry();
  737.     if (uep == NULL)
  738.     return;
  739.  
  740.     if (uep->ue_lcount != 0)
  741.     {
  742.     /*
  743.      * the new ue_bot is computed from the number of lines that has been
  744.      * inserted (0 - deleted) since calling u_save. This is equal to the old
  745.      * line count subtracted from the current line count.
  746.      */
  747.     uep->ue_bot = uep->ue_top + uep->ue_size + 1 +
  748.                 (curbuf->b_ml.ml_line_count - uep->ue_lcount);
  749.     if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count)
  750.     {
  751.         EMSG(_("E440: undo line missing"));
  752.         uep->ue_bot = uep->ue_top + 1;  /* assume all lines deleted, will
  753.                          * get all the old lines back
  754.                          * without deleting the current
  755.                          * ones */
  756.     }
  757.     uep->ue_lcount = 0;
  758.     }
  759.  
  760.     curbuf->b_u_synced = TRUE;
  761. }
  762.  
  763. /*
  764.  * u_freelist: free one entry list and adjust the pointers
  765.  */
  766.     static void
  767. u_freelist(uhp)
  768.     struct u_header *uhp;
  769. {
  770.     u_entry_T    *uep, *nuep;
  771.  
  772.     for (uep = uhp->uh_entry; uep != NULL; uep = nuep)
  773.     {
  774.     nuep = uep->ue_next;
  775.     u_freeentry(uep, uep->ue_size);
  776.     }
  777.  
  778.     if (curbuf->b_u_curhead == uhp)
  779.     curbuf->b_u_curhead = NULL;
  780.  
  781.     if (uhp->uh_next == NULL)
  782.     curbuf->b_u_oldhead = uhp->uh_prev;
  783.     else
  784.     uhp->uh_next->uh_prev = uhp->uh_prev;
  785.  
  786.     if (uhp->uh_prev == NULL)
  787.     curbuf->b_u_newhead = uhp->uh_next;
  788.     else
  789.     uhp->uh_prev->uh_next = uhp->uh_next;
  790.  
  791.     u_free_line((char_u *)uhp, FALSE);
  792.     --curbuf->b_u_numhead;
  793. }
  794.  
  795. /*
  796.  * free entry 'uep' and 'n' lines in uep->ue_array[]
  797.  */
  798.     static void
  799. u_freeentry(uep, n)
  800.     u_entry_T    *uep;
  801.     long        n;
  802. {
  803.     while (n)
  804.     u_free_line(uep->ue_array[--n], FALSE);
  805.     u_free_line((char_u *)uep, FALSE);
  806. }
  807.  
  808. /*
  809.  * invalidate the undo buffer; called when storage has already been released
  810.  */
  811.     void
  812. u_clearall(buf)
  813.     buf_T    *buf;
  814. {
  815.     buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
  816.     buf->b_u_synced = TRUE;
  817.     buf->b_u_numhead = 0;
  818.     buf->b_u_line_ptr = NULL;
  819.     buf->b_u_line_lnum = 0;
  820. }
  821.  
  822. /*
  823.  * save the line "lnum" for the "U" command
  824.  */
  825.     void
  826. u_saveline(lnum)
  827.     linenr_T lnum;
  828. {
  829.     if (lnum == curbuf->b_u_line_lnum)        /* line is already saved */
  830.     return;
  831.     if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)    /* should never happen */
  832.     return;
  833.     u_clearline();
  834.     curbuf->b_u_line_lnum = lnum;
  835.     if (curwin->w_cursor.lnum == lnum)
  836.     curbuf->b_u_line_colnr = curwin->w_cursor.col;
  837.     else
  838.     curbuf->b_u_line_colnr = 0;
  839.     if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL)
  840.     do_outofmem_msg((long_u)0);
  841. }
  842.  
  843. /*
  844.  * clear the line saved for the "U" command
  845.  * (this is used externally for crossing a line while in insert mode)
  846.  */
  847.     void
  848. u_clearline()
  849. {
  850.     if (curbuf->b_u_line_ptr != NULL)
  851.     {
  852.     u_free_line(curbuf->b_u_line_ptr, FALSE);
  853.     curbuf->b_u_line_ptr = NULL;
  854.     curbuf->b_u_line_lnum = 0;
  855.     }
  856. }
  857.  
  858. /*
  859.  * Implementation of the "U" command.
  860.  * Differentiation from vi: "U" can be undone with the next "U".
  861.  * We also allow the cursor to be in another line.
  862.  */
  863.     void
  864. u_undoline()
  865. {
  866.     colnr_T t;
  867.     char_u  *oldp;
  868.  
  869.     if (undo_off)
  870.     return;
  871.  
  872.     if (curbuf->b_u_line_ptr == NULL ||
  873.             curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count)
  874.     {
  875.     beep_flush();
  876.     return;
  877.     }
  878.     /* first save the line for the 'u' command */
  879.     if (u_savecommon(curbuf->b_u_line_lnum - 1,
  880.                 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL)
  881.     return;
  882.     oldp = u_save_line(curbuf->b_u_line_lnum);
  883.     if (oldp == NULL)
  884.     {
  885.     do_outofmem_msg((long_u)0);
  886.     return;
  887.     }
  888.     ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE);
  889.     changed_bytes(curbuf->b_u_line_lnum, 0);
  890.     u_free_line(curbuf->b_u_line_ptr, FALSE);
  891.     curbuf->b_u_line_ptr = oldp;
  892.  
  893.     t = curbuf->b_u_line_colnr;
  894.     if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum)
  895.     curbuf->b_u_line_colnr = curwin->w_cursor.col;
  896.     curwin->w_cursor.col = t;
  897.     curwin->w_cursor.lnum = curbuf->b_u_line_lnum;
  898. }
  899.  
  900. /*
  901.  * storage allocation for the undo lines and blocks of the current file
  902.  */
  903.  
  904. /*
  905.  * Memory is allocated in relatively large blocks. These blocks are linked
  906.  * in the allocated block list, headed by curbuf->b_block_head. They are all
  907.  * freed when abandoning a file, so we don't have to free every single line.
  908.  * The list is kept sorted on memory address.
  909.  * block_alloc() allocates a block.
  910.  * m_blockfree() frees all blocks.
  911.  *
  912.  * The available chunks of memory are kept in free chunk lists. There is
  913.  * one free list for each block of allocated memory. The list is kept sorted
  914.  * on memory address.
  915.  * u_alloc_line() gets a chunk from the free lists.
  916.  * u_free_line() returns a chunk to the free lists.
  917.  * curbuf->b_m_search points to the chunk before the chunk that was
  918.  * freed/allocated the last time.
  919.  * curbuf->b_mb_current points to the b_head where curbuf->b_m_search
  920.  * points into the free list.
  921.  *
  922.  *
  923.  *  b_block_head     /---> block #1    /---> block #2
  924.  *     mb_next ---/        mb_next ---/       mb_next ---> NULL
  925.  *     mb_info        mb_info           mb_info
  926.  *        |               |          |
  927.  *        V               V          V
  928.  *      NULL        free chunk #1.1      free chunk #2.1
  929.  *                   |          |
  930.  *                   V          V
  931.  *            free chunk #1.2         NULL
  932.  *                   |
  933.  *                   V
  934.  *                  NULL
  935.  *
  936.  * When a single free chunk list would have been used, it could take a lot
  937.  * of time in u_free_line() to find the correct place to insert a chunk in the
  938.  * free list. The single free list would become very long when many lines are
  939.  * changed (e.g. with :%s/^M$//).
  940.  */
  941.  
  942.  /*
  943.   * this blocksize is used when allocating new lines
  944.   */
  945. #define MEMBLOCKSIZE 2044
  946.  
  947. /*
  948.  * The size field contains the size of the chunk, including the size field
  949.  * itself.
  950.  *
  951.  * When the chunk is not in-use it is preceded with the m_info structure.
  952.  * The m_next field links it in one of the free chunk lists.
  953.  *
  954.  * On most unix systems structures have to be longword (32 or 64 bit) aligned.
  955.  * On most other systems they are short (16 bit) aligned.
  956.  */
  957.  
  958. /* the structure definitions are now in structs.h */
  959.  
  960. #ifdef ALIGN_LONG
  961.     /* size of m_size */
  962. # define M_OFFSET (sizeof(long_u))
  963. #else
  964.     /* size of m_size */
  965. # define M_OFFSET (sizeof(short_u))
  966. #endif
  967.  
  968. /*
  969.  * Allocate a block of memory and link it in the allocated block list.
  970.  */
  971.     static char_u *
  972. u_blockalloc(size)
  973.     long_u    size;
  974. {
  975.     mblock_T    *p;
  976.     mblock_T    *mp, *next;
  977.  
  978.     p = (mblock_T *)lalloc(size + sizeof(mblock_T), FALSE);
  979.     if (p != NULL)
  980.     {
  981.      /* Insert the block into the allocated block list, keeping it
  982.             sorted on address. */
  983.     for (mp = &curbuf->b_block_head;
  984.         (next = mp->mb_next) != NULL && next < p;
  985.             mp = next)
  986.         ;
  987.     p->mb_next = next;        /* link in block list */
  988.     p->mb_size = size;
  989.     mp->mb_next = p;
  990.     p->mb_info.m_next = NULL;    /* clear free list */
  991.     p->mb_info.m_size = 0;
  992.     curbuf->b_mb_current = p;    /* remember current block */
  993.     curbuf->b_m_search = NULL;
  994.     p++;                /* return usable memory */
  995.     }
  996.     return (char_u *)p;
  997. }
  998.  
  999. /*
  1000.  * free all allocated memory blocks for the buffer 'buf'
  1001.  */
  1002.     void
  1003. u_blockfree(buf)
  1004.     buf_T    *buf;
  1005. {
  1006.     mblock_T    *p, *np;
  1007.  
  1008.     for (p = buf->b_block_head.mb_next; p != NULL; p = np)
  1009.     {
  1010.     np = p->mb_next;
  1011.     vim_free(p);
  1012.     }
  1013.     buf->b_block_head.mb_next = NULL;
  1014.     buf->b_m_search = NULL;
  1015.     buf->b_mb_current = NULL;
  1016. }
  1017.  
  1018. /*
  1019.  * Free a chunk of memory for the current buffer.
  1020.  * Insert the chunk into the correct free list, keeping it sorted on address.
  1021.  */
  1022.     static void
  1023. u_free_line(ptr, keep)
  1024.     char_u    *ptr;
  1025.     int        keep;    /* don't free the block when it's empty */
  1026. {
  1027.     minfo_T    *next;
  1028.     minfo_T    *prev, *curr;
  1029.     minfo_T    *mp;
  1030.     mblock_T    *nextb;
  1031.     mblock_T    *prevb;
  1032.  
  1033.     if (ptr == NULL || ptr == IObuff)
  1034.     return;    /* illegal address can happen in out-of-memory situations */
  1035.  
  1036.     mp = (minfo_T *)(ptr - M_OFFSET);
  1037.  
  1038.     /* find block where chunk could be a part off */
  1039.     /* if we change curbuf->b_mb_current, curbuf->b_m_search is set to NULL */
  1040.     if (curbuf->b_mb_current == NULL || mp < (minfo_T *)curbuf->b_mb_current)
  1041.     {
  1042.     curbuf->b_mb_current = curbuf->b_block_head.mb_next;
  1043.     curbuf->b_m_search = NULL;
  1044.     }
  1045.     if ((nextb = curbuf->b_mb_current->mb_next) != NULL
  1046.                              && (minfo_T *)nextb < mp)
  1047.     {
  1048.     curbuf->b_mb_current = nextb;
  1049.     curbuf->b_m_search = NULL;
  1050.     }
  1051.     while ((nextb = curbuf->b_mb_current->mb_next) != NULL
  1052.                              && (minfo_T *)nextb < mp)
  1053.     curbuf->b_mb_current = nextb;
  1054.  
  1055.     curr = NULL;
  1056.     /*
  1057.      * If mp is smaller than curbuf->b_m_search->m_next go to the start of
  1058.      * the free list
  1059.      */
  1060.     if (curbuf->b_m_search == NULL || mp < (curbuf->b_m_search->m_next))
  1061.     next = &(curbuf->b_mb_current->mb_info);
  1062.     else
  1063.     next = curbuf->b_m_search;
  1064.     /*
  1065.      * The following loop is executed very often.
  1066.      * Therefore it has been optimized at the cost of readability.
  1067.      * Keep it fast!
  1068.      */
  1069. #ifdef SLOW_BUT_EASY_TO_READ
  1070.     do
  1071.     {
  1072.     prev = curr;
  1073.     curr = next;
  1074.     next = next->m_next;
  1075.     }
  1076.     while (mp > next && next != NULL);
  1077. #else
  1078.     do                        /* first, middle, last */
  1079.     {
  1080.     prev = next->m_next;            /* curr, next, prev */
  1081.     if (prev == NULL || mp <= prev)
  1082.     {
  1083.         prev = curr;
  1084.         curr = next;
  1085.         next = next->m_next;
  1086.         break;
  1087.     }
  1088.     curr = prev->m_next;            /* next, prev, curr */
  1089.     if (curr == NULL || mp <= curr)
  1090.     {
  1091.         prev = next;
  1092.         curr = prev->m_next;
  1093.         next = curr->m_next;
  1094.         break;
  1095.     }
  1096.     next = curr->m_next;            /* prev, curr, next */
  1097.     }
  1098.     while (mp > next && next != NULL);
  1099. #endif
  1100.  
  1101.     /* if *mp and *next are concatenated, join them into one chunk */
  1102.     if ((char_u *)mp + mp->m_size == (char_u *)next)
  1103.     {
  1104.     mp->m_size += next->m_size;
  1105.     mp->m_next = next->m_next;
  1106.     }
  1107.     else
  1108.     mp->m_next = next;
  1109.  
  1110.     /* if *curr and *mp are concatenated, join them */
  1111.     if (prev != NULL && (char_u *)curr + curr->m_size == (char_u *)mp)
  1112.     {
  1113.     curr->m_size += mp->m_size;
  1114.     curr->m_next = mp->m_next;
  1115.     curbuf->b_m_search = prev;
  1116.     }
  1117.     else
  1118.     {
  1119.     curr->m_next = mp;
  1120.     curbuf->b_m_search = curr;  /* put curbuf->b_m_search before freed
  1121.                        chunk */
  1122.     }
  1123.  
  1124.     /*
  1125.      * If the block only containes free memory now, release it.
  1126.      */
  1127.     if (!keep && curbuf->b_mb_current->mb_size
  1128.                   == curbuf->b_mb_current->mb_info.m_next->m_size)
  1129.     {
  1130.     /* Find the block before the current one to be able to unlink it from
  1131.      * the list of blocks. */
  1132.     prevb = &curbuf->b_block_head;
  1133.     for (nextb = prevb->mb_next; nextb != curbuf->b_mb_current;
  1134.                                nextb = nextb->mb_next)
  1135.         prevb = nextb;
  1136.     prevb->mb_next = nextb->mb_next;
  1137.     vim_free(nextb);
  1138.     curbuf->b_mb_current = NULL;
  1139.     curbuf->b_m_search = NULL;
  1140.     }
  1141. }
  1142.  
  1143. /*
  1144.  * Allocate and initialize a new line structure with room for at least
  1145.  * 'size' characters plus a terminating NUL.
  1146.  */
  1147.     static char_u *
  1148. u_alloc_line(size)
  1149.     unsigned    size;
  1150. {
  1151.     minfo_T    *mp, *mprev, *mp2;
  1152.     mblock_T    *mbp;
  1153.     int        size_align;
  1154.  
  1155.     /*
  1156.      * Add room for size field and trailing NUL byte.
  1157.      * Adjust for minimal size (must be able to store minfo_T
  1158.      * plus a trailing NUL, so the chunk can be released again)
  1159.      */
  1160.     size += M_OFFSET + 1;
  1161.     if (size < sizeof(minfo_T) + 1)
  1162.     size = sizeof(minfo_T) + 1;
  1163.  
  1164.     /*
  1165.      * round size up for alignment
  1166.      */
  1167.     size_align = (size + ALIGN_MASK) & ~ALIGN_MASK;
  1168.  
  1169.     /*
  1170.      * If curbuf->b_m_search is NULL (uninitialized free list) start at
  1171.      * curbuf->b_block_head
  1172.      */
  1173.     if (curbuf->b_mb_current == NULL || curbuf->b_m_search == NULL)
  1174.     {
  1175.     curbuf->b_mb_current = &curbuf->b_block_head;
  1176.     curbuf->b_m_search = &(curbuf->b_block_head.mb_info);
  1177.     }
  1178.  
  1179.     /* search for space in free list */
  1180.     mprev = curbuf->b_m_search;
  1181.     mbp = curbuf->b_mb_current;
  1182.     mp = curbuf->b_m_search->m_next;
  1183.     if (mp == NULL)
  1184.     {
  1185.     if (mbp->mb_next)
  1186.         mbp = mbp->mb_next;
  1187.     else
  1188.         mbp = &curbuf->b_block_head;
  1189.     mp = curbuf->b_m_search = &(mbp->mb_info);
  1190.     }
  1191.     while (mp->m_size < size)
  1192.     {
  1193.     if (mp == curbuf->b_m_search)        /* back where we started in free
  1194.                            chunk list */
  1195.     {
  1196.         if (mbp->mb_next)
  1197.         mbp = mbp->mb_next;
  1198.         else
  1199.         mbp = &curbuf->b_block_head;
  1200.         mp = curbuf->b_m_search = &(mbp->mb_info);
  1201.         if (mbp == curbuf->b_mb_current)    /* back where we started in
  1202.                            block list */
  1203.         {
  1204.         int    n = (size_align > (MEMBLOCKSIZE / 4)
  1205.                          ? size_align : MEMBLOCKSIZE);
  1206.  
  1207.         mp = (minfo_T *)u_blockalloc((long_u)n);
  1208.         if (mp == NULL)
  1209.             return (NULL);
  1210.         mp->m_size = n;
  1211.         u_free_line((char_u *)mp + M_OFFSET, TRUE);
  1212.         mp = curbuf->b_m_search;
  1213.         mbp = curbuf->b_mb_current;
  1214.         }
  1215.     }
  1216.     mprev = mp;
  1217.     if ((mp = mp->m_next) == NULL)        /* at end of the list */
  1218.         mp = &(mbp->mb_info);        /* wrap around to begin */
  1219.     }
  1220.  
  1221.     /* if the chunk we found is large enough, split it up in two */
  1222.     if ((long)mp->m_size - size_align >= (long)(sizeof(minfo_T) + 1))
  1223.     {
  1224.     mp2 = (minfo_T *)((char_u *)mp + size_align);
  1225.     mp2->m_size = mp->m_size - size_align;
  1226.     mp2->m_next = mp->m_next;
  1227.     mprev->m_next = mp2;
  1228.     mp->m_size = size_align;
  1229.     }
  1230.     else            /* remove *mp from the free list */
  1231.     {
  1232.     mprev->m_next = mp->m_next;
  1233.     }
  1234.     curbuf->b_m_search = mprev;
  1235.     curbuf->b_mb_current = mbp;
  1236.  
  1237.     mp = (minfo_T *)((char_u *)mp + M_OFFSET);
  1238.     *(char_u *)mp = NUL;            /* set the first byte to NUL */
  1239.  
  1240.     return ((char_u *)mp);
  1241. }
  1242.  
  1243. /*
  1244.  * u_save_line(): allocate memory with u_alloc_line() and copy line 'lnum'
  1245.  * into it.
  1246.  */
  1247.     static char_u *
  1248. u_save_line(lnum)
  1249.     linenr_T    lnum;
  1250. {
  1251.     char_u    *src;
  1252.     char_u    *dst;
  1253.     unsigned    len;
  1254.  
  1255.     src = ml_get(lnum);
  1256.     len = (unsigned)STRLEN(src);
  1257.     if ((dst = u_alloc_line(len)) != NULL)
  1258.     mch_memmove(dst, src, (size_t)(len + 1));
  1259.     return (dst);
  1260. }
  1261.  
  1262. /*
  1263.  * Check if the 'modified' flag is set, or 'ff' has changed (only need to
  1264.  * check the first character, because it can only be "dos", "unix" or "mac").
  1265.  * "nofile" and "scratch" type buffers are considered to always be unchanged.
  1266.  */
  1267.     int
  1268. bufIsChanged(buf)
  1269.     buf_T    *buf;
  1270. {
  1271.     return
  1272. #ifdef FEAT_QUICKFIX
  1273.         !bt_dontwrite(buf) &&
  1274. #endif
  1275.         (buf->b_changed || file_ff_differs(buf));
  1276. }
  1277.  
  1278.     int
  1279. curbufIsChanged()
  1280. {
  1281.     return
  1282. #ifdef FEAT_QUICKFIX
  1283.     !bt_dontwrite(curbuf) &&
  1284. #endif
  1285.     (curbuf->b_changed || file_ff_differs(curbuf));
  1286. }
  1287.